﻿/**
 * Copyright (C) 2012-2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Internal.Util;
using System.Reflection;

namespace NGuice.Inject.Matcher
{
    [Serializable]
    internal class Any : AbstractMatcher<object>
    {
        public override string ToString()
        {
            return "any()";
        }

        public override bool Match(object t)
        {
            return true;
        }
    }

    [Serializable]
    internal class Not<T> : AbstractMatcher<T>
    {
        readonly IMatcher delegatee;

        public Not(IMatcher delegatee)
        {
            this.delegatee = Preconditions.CheckNotNull(delegatee, "delegate");
            Preconditions.CheckSuperGenericParameter<T>(delegatee);
        }

        public override bool Match(T t)
        {
            return !delegatee.Matches(t);
        }

        public override bool Equals(object other)
        {
            return other is Not<T>
                && ((Not<T>)other).delegatee.Equals(delegatee);
        }

        public override int GetHashCode()
        {
            return -delegatee.GetHashCode();
        }

        public override string ToString()
        {
            return "not(" + delegatee + ")";
        }
    }

    [Serializable]
    internal class AnnotatedWith : AbstractMatcher<ICustomAttributeProvider>
    {
        private readonly Attribute annotation;

        public AnnotatedWith(Attribute annotation)
        {
            this.annotation = Preconditions.CheckNotNull(annotation, "annotation");
            //checkForRuntimeRetention(annotation.annotationType());
        }

        public override bool Match(ICustomAttributeProvider element)
        {
            object[] fromElement = element.GetCustomAttributes(annotation.GetType(), false);
            if (fromElement == null)
                return false;
            bool find = false;
            foreach (object f in fromElement)
            {
                if (annotation.Equals(fromElement))
                {
                    find = true;
                    break;
                }
            }
            return find;
        }

        public override bool Equals(object other)
        {
            return other is AnnotatedWith
                && ((AnnotatedWith)other).annotation.Equals(annotation);
        }

        public override int GetHashCode()
        {
            return 37 * annotation.GetHashCode();
        }

        public override string ToString()
        {
            return "annotatedWith(" + annotation + ")";
        }
    }

    [Serializable]
    internal class AnnotatedWithType : AbstractMatcher<ICustomAttributeProvider>
    {
        private readonly Type annotationType;

        public AnnotatedWithType(Type annotationType)
        {
            this.annotationType = Preconditions.CheckNotNull(annotationType, "annotation type");
            //checkForRuntimeRetention(annotationType);
        }

        public override bool Match(ICustomAttributeProvider element)
        {
            object[] attributes = element.GetCustomAttributes(annotationType, false);
            return attributes != null && attributes.Length != 0;
        }

        public override bool Equals(object other)
        {
            return other is AnnotatedWithType
                && ((AnnotatedWithType)other).annotationType.Equals(annotationType);
        }

        public override int GetHashCode()
        {
            return 37 * annotationType.GetHashCode();
        }

        public override string ToString()
        {
            return "annotatedWith(" + annotationType.Name + ".class)";
        }
    }

    [Serializable]
    internal class SubclassesOf : AbstractMatcher<Type>
    {
        private readonly Type superclass;

        public SubclassesOf(Type superclass)
        {
            this.superclass = Preconditions.CheckNotNull(superclass, "superclass");
        }

        public override bool Match(Type subclass)
        {
            return superclass.IsAssignableFrom(subclass);
        }

        public override bool Equals(object other)
        {
            return other is SubclassesOf
                && ((SubclassesOf)other).superclass.Equals(superclass);
        }

        public override int GetHashCode()
        {
            return 37 * superclass.GetHashCode();
        }

        public override string ToString()
        {
            return "subclassesOf(" + superclass.Name + ".class)";
        }
    }

    [Serializable]
    internal class Only : AbstractMatcher<object>
    {
        private readonly object value;

        public Only(object value)
        {
            this.value = Preconditions.CheckNotNull(value, "value");
        }

        public override bool Match(object other)
        {
            return value.Equals(other);
        }

        public override bool Equals(object other)
        {
            return other is Only
                && ((Only)other).value.Equals(value);
        }

        public override int GetHashCode()
        {
            return 37 * value.GetHashCode();
        }

        public override string ToString()
        {
            return "only(" + value + ")";
        }
    }

    [Serializable]
    internal class IdenticalTo : AbstractMatcher<object>
    {
        private readonly object value;

        public IdenticalTo(object value)
        {
            this.value = Preconditions.CheckNotNull(value, "value");
        }

        public override bool Match(object other)
        {
            return value == other;
        }

        public override bool Equals(object other)
        {
            return other is IdenticalTo
                && ((IdenticalTo)other).value == value;
        }

        public override int GetHashCode()
        {
            return 37 * value.GetHashCode();
        }

        public override string ToString()
        {
            return "identicalTo(" + value + ")";
        }
    }

    [Serializable]
    internal class InAssembly : AbstractMatcher<Type>
    {
        private readonly Assembly targetPackage;

        public InAssembly(Assembly targetPackage)
        {
            this.targetPackage = Preconditions.CheckNotNull(targetPackage, "package");
            //this.packageName = targetPackage.FullName + ", " + space;
        }

        public override bool Match(Type c)
        {
            return c.Assembly.Equals(targetPackage);
        }

        public override bool Equals(object other)
        {
            return other is InAssembly
                && ((InAssembly)other).targetPackage.Equals(targetPackage);
        }

        public override int GetHashCode()
        {
            return 37 * targetPackage.GetHashCode();
        }

        public override string ToString()
        {
            return "inPackage(" + targetPackage.FullName + ")";
        }
    }

    [Serializable]
    internal class InSubNamespace : AbstractMatcher<Type>
    {
        private readonly string targetPackageName;

        public InSubNamespace(string targetPackageName)
        {
            this.targetPackageName = targetPackageName;
        }

        public override bool Match(Type c)
        {
            string classPackageName = c.Namespace;
            return classPackageName.Equals(targetPackageName)
                || classPackageName.StartsWith(targetPackageName + ".");
        }

        public override bool Equals(object other)
        {
            return other is InSubNamespace
                && ((InSubNamespace)other).targetPackageName.Equals(targetPackageName);
        }

        public override int GetHashCode()
        {
            return 37 * targetPackageName.GetHashCode();
        }

        public override string ToString()
        {
            return "inSubpackage(" + targetPackageName + ")";
        }
    }

    [Serializable]
    internal class Returns : AbstractMatcher<MethodInfo>
    {
        private readonly IMatcher<Type> returnType;

        public Returns(IMatcher<Type> returnType)
        {
            this.returnType = Preconditions.CheckNotNull(returnType, "return type matcher");
        }

        public override bool Match(MethodInfo m)
        {
            return returnType.Match(m.ReturnType);
        }

        public override bool Equals(object other)
        {
            return other is Returns
                && ((Returns)other).returnType.Equals(returnType);
        }

        public int GetHashCode()
        {
            return 37 * returnType.GetHashCode();
        }

        public string ToString()
        {
            return "returns(" + returnType + ")";
        }

    }

    /// <summary>
    /// 匹配器的各种实现，支持匹配类和方法
    /// </summary>
    public sealed class Matchers
    {
        private Matchers() { }

        /// <summary>
        /// 匹配所有输入的匹配器
        /// </summary>
        /// <returns></returns>
        public static IMatcher<object> Any()
        {
            return ANY;
        }

        private static readonly IMatcher<object> ANY = new Any();


        public static IMatcher<T> Not<S, T>(IMatcher<S> p)
            where T : S
        {
            return new Not<T>(p);
        }

        /// <summary>
        /// 返回一个匹配器，此匹配其能够匹配给定元特性的方法、类等元素
        /// </summary>
        /// <param name="annotationType"></param>
        /// <returns></returns>
        public static IMatcher<ICustomAttributeProvider> AnnotatedWith(Type annotationType)
        {
            return new AnnotatedWithType(annotationType);
        }

        /// <summary>
        /// 返回一个匹配器，此匹配其能够匹配给定元特性的方法、类等元素
        /// </summary>
        /// <param name="annotation"></param>
        /// <returns></returns>
        public static IMatcher<ICustomAttributeProvider> AnnotatedWith(Attribute annotation)
        {
            return new AnnotatedWith(annotation);
        }

        /// <summary>
        /// 返回匹配给定类型的子类的匹配器
        /// </summary>
        /// <param name="superclass"></param>
        /// <returns></returns>
        public static IMatcher<Type> SubclassesOf(Type superclass)
        {
            return new SubclassesOf(superclass);
        }

        /// <summary>
        /// 返回等于给定对象的匹配器
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static IMatcher<object> Only(object value)
        {
            return new Only(value);
        }

        /// <summary>
        /// 返回仅匹配给定对象的匹配器
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static IMatcher<object> IdenticalTo(object value)
        {
            return new IdenticalTo(value);
        }

        /// <summary>
        /// 返回一个在给定名称空间下匹配类的匹配器。程序集特定于加载器，因此具有相同名称空间的类可能在运行时具有不同的程序集
        /// </summary>
        /// <param name="targetPackage"></param>
        /// <returns></returns>
        public static IMatcher<Type> InAssembly(Assembly targetPackage)
        {
            return new InAssembly(targetPackage);
        }

        /// <summary>
        /// 返回在给定的子空间下匹配的匹配器，他不像InAssembly，他不匹配程序集，只匹配名称空间或子空间
        /// </summary>
        /// <param name="targetPackageName"></param>
        /// <returns></returns>
        public static IMatcher<Type> InSubNamespace(string targetPackageName)
        {
            return new InSubNamespace(targetPackageName);
        }

        /// <summary>
        /// 返回一个匹配器，此匹配器匹配给定返回类型的方法
        /// </summary>
        /// <param name="returnType"></param>
        /// <returns></returns>
        public static IMatcher<MethodInfo> Returns(IMatcher<Type> returnType)
        {
            return new Returns(returnType);
        }

    }
}
